/*******************************************************************
 * author   :  Charlies-ls
 * Date     :  2021-09-26
 * Copyright (c) 2021. artestplay@outlook.com. All Rights Reserved.
 *******************************************************************/
#include "ax_vector.h"
#include "ax_printf.h"
#include "com.h"
#include <stdlib.h>
#ifdef _WIN32
#else
#include <unistd.h>
#endif
#include <string.h>

#define IS_HOLD_DATA(v) (v->item_size>0)
/* struct for vector */
struct ax_vector
{
    size_t   active;    /* items used                          */
    size_t   alloced;   /* memory size have alloced            */
    size_t   item_size; /* keep pointer only when item_size==0 */
    void**   index;     /* pointers hold data                  */
    uint8_t* data;      /* keep data when item_size>0          */
} ;

ax_vector* ax_vector_create(size_t item_count, size_t item_size)
{
    ax_vector* v = ALLOC(ax_vector);
    ax_check_and_return_zero(v, "error");

    if(item_count == 0)
        item_count = 1;

    v->alloced   = item_count;
    v->item_size = item_size;
    v->active    = 0;
    v->index     = malloc(sizeof(void*) * item_count);
    v->data      = NULL;
    if(!v->index)
    {
        free(v);
        return NULL;
    }

    /* need keep data */
    if(IS_HOLD_DATA(v))
    {
        size_t i;
        v->data = (uint8_t*)malloc(v->item_size * item_count);
        if(NULL == v->data)
        {
            free(v->index);
        }
        /* map the data and pointer */
        for(i = 0; i < item_count; i++)
        {
            v->index[i] = v->data + i * item_size;
        }
    }

    return v;
}

void ax_vector_destory(ax_vector* v)
{
    ax_check_and_return(v, "empty");
    free(v->index);
    if(IS_HOLD_DATA(v))
    {
        free(v->data);
    }
    free(v);
}

void ax_vector_content_destory(ax_vector* v)
{
    ax_check_and_return(v, "empty");
    for(size_t i = 0; i < ax_vector_size(v); i++)
    {
        void* d = ax_vector_at(v, i);
        free(d);
    }
}

/**
 * @brief grow double until met
 *        if new_size < size, tail data will lost
 * @param v
 * @param new size
 */
size_t ax_vector_resize(ax_vector* v, size_t new_size)
{
    if(new_size <= v->alloced)
    {
        v->active = new_size;
        return new_size;
    }
    ax_vector_reserve(v, new_size);
    v->active = new_size;
    return new_size;
}

/**
 * @brief reserve space, elements count unchange
 * @param v
 * @param new_size
 */
size_t ax_vector_reserve(ax_vector* v, size_t new_size)
{
    ax_check_and_return_zero(v, NULL);

    if(new_size <= v->alloced)
        return new_size;

    while(v->alloced < new_size)
    {
        v->alloced *= 2;
    }
    v->index = realloc(v->index, sizeof(void*) * v->alloced);

    if(IS_HOLD_DATA(v))
    {
        v->data = realloc(v->data, v->item_size * v->alloced);
    }

    memset(&v->index[v->active], 0, sizeof(void*) * (v->alloced - v->active - 1));
    return v->alloced;
}
/**
 * @brief  append data to end
 * @param  v
 * @param  val
 * @return if ok, return size of vector, if not return 0
 */
size_t ax_vector_push_back(ax_vector* v, void* val)
{
    ax_check_and_return_zero(v, "error");
    ax_vector_reserve(v, v->active + 1);
    if(IS_HOLD_DATA(v))
    {
        memcpy(v->index[v->active++], val, v->item_size);
    }
    else
    {
        v->index[v->active++] = val;
    }
    return v->active;
}

/**
 * @brief can only set below active index
 * @param v
 * @param i
 * @param val
 * @return if ok, return size, if not return 0
 */
size_t ax_vector_set(ax_vector* v, size_t i, void* val)
{
    ax_check_and_return_zero(v && (i < v->active), "error");
    if(IS_HOLD_DATA(v))
    {
        memcpy(v->index[i], val, v->item_size);
    }
    v->index[i] = val;
    return v->active;
}

void* ax_vector_at(ax_vector* v, size_t i)
{
    if(i >= v->active)
        return NULL;
    return v->index[i];
}

/* Count the number of not emplty slot. */
size_t ax_vector_size(ax_vector* v)
{
    ax_check_and_return_zero(v, "empty");
    return v->active;
}

/**
 * @brief ax_vector_erase
 * @param v
 * @param index
 * @return data ptr deleted
 */
void* ax_vector_erase(ax_vector* v, size_t index)
{
    if(NULL == v || index >= v->active)
        return NULL;
    void* data = v->index[index];
    memcpy(&v->index[index], &v->index[index + 1], (v->active - index - 1) * sizeof(void*));
    if(IS_HOLD_DATA(v))
    {
        memcpy(v->index[index], v->index[index + 1], (v->active - index - 1) * v->item_size);
    }
    v->active--;
    return data;
}

void ax_vector_clear(ax_vector* v)
{
    ax_check_and_return(v, "empty");
    v->active = 0;
}
